package com.gitee.wsl.model.process.request

import com.gitee.wsl.model.process.call.ProcessClientCall
import com.gitee.wsl.prop.Attributes
import com.gitee.wsl.prop.putAll
import com.gitee.wsl.reflect.TypeInfo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlin.coroutines.CoroutineContext
import kotlin.reflect.KType

/**
 * A request for [HttpClient], first part of [ProcessClientCall].
 */
 interface ProcessRequest : CoroutineScope {
    /**
     * The associated [ProcessClientCall] containing both
     * the underlying [ProcessClientCall.request] and [ProcessClientCall.processResponse].
     */
     val call: ProcessClientCall

    override val coroutineContext: CoroutineContext get() = call.coroutineContext
    
    /**
     * Typed [Attributes] associated to this call serving as a lightweight container.
     */
     val attributes: Attributes

    /**
     * An [OutgoingContent] representing the request body
     */
     val content: Any
}

val NullBody = Unit

open class ProcessRequestBuilder{
    /**
     * The [body] for this request. Initially [EmptyContent].
     */
    var body: Any = NullBody
    
    /**
     * The [KType] of [body] for this request. Null for default types that don't need serialization.
     */
     var bodyType: TypeInfo?
        get() = attributes.getOrNull(BodyTypeAttributeKey)

        set(value) {
            if (value != null) {
                attributes.put(BodyTypeAttributeKey, value)
            } else {
                attributes.remove(BodyTypeAttributeKey)
            }
        }

    /**
     * A deferred used to control the execution of this request.
     */
     var executionContext: Job = SupervisorJob()
        internal set

    /**
     * Provides access to attributes specific for this request.
     */
     val attributes: Attributes = Attributes(concurrent = true)

    /**
     * Sets request-specific attributes specified by [block].
     */
     fun setAttributes(block: Attributes.() -> Unit) {
        attributes.apply(block)
    }

    /**
     * Mutates [this] copying all the data from another [builder] using it as the base.
     */
     fun takeFromWithExecutionContext(builder: ProcessRequestBuilder): ProcessRequestBuilder {
        executionContext = builder.executionContext
        return takeFrom(builder)
    }

    /**
     * Mutates [this] by copying all the data but execution context from another [builder] using it as the base.
     */

    fun takeFrom(builder: ProcessRequestBuilder): ProcessRequestBuilder {
        body = builder.body
        bodyType = builder.bodyType
        attributes.putAll(builder.attributes)
        return this
    }

     companion object
}